﻿using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace FiveInRow
{
    class Trainer : ITrainer
    {
        public int ChessSize { get { return gridSize; } }

        Factory factory = new Factory();
        IChessStatus chessStatus;
        int gridSize;
        bool blackTurn=true;
        int games;
        int gameCount = 0;
        int oneGameRound = 0;

        UnManagedWrapper.StrategyWrapper strategyWrapper;

        UnManagedWrapper.ChessTrainerWrapper trainerWrapper;

        log4net.ILog log = log4net.LogManager.GetLogger("MyLogger");

        public Trainer(int games)
        {
            this.games = games;
        }

        public void SetTrainer(UnManagedWrapper.ChessTrainerWrapper trainer)
        {
            this.trainerWrapper = trainer;
        }

        public void Start()
        {
            log.Info("--------------Start Training!------------");
            PrepairChessBoard();

            //First step.
            FirstStep();
        }

        public void OneStep()
        {
            if(EndGame())
            {
                log.InfoFormat("Game round: {0}", oneGameRound);
                ResetGame();
                //Make first step.
                FirstStep();
                ++gameCount;
            }
            else
            {
                if (blackTurn)
                {
                    //black turn
                    strategyWrapper.Update(chessStatus);
                    int nextX = 0;
                    int nextY = 0;
                    strategyWrapper.NextPoint(chessStatus, ref nextX, ref nextY);
                    chessStatus.AddBlackPiece(CreateBlackPiece(nextX, nextY));
                    blackTurn = false;
                }
                else
                {
                    //white turn
                    //Check and update winning rate of white and decide an optimal step.
                    strategyWrapper.Update(chessStatus);
                    int nextX = 0, nextY = 0;
                    strategyWrapper.NextPoint(chessStatus, ref nextX, ref nextY);
                    chessStatus.AddWhitePiece(CreateWhitePiece(nextX, nextY));
                    blackTurn = true;
                }
                ++oneGameRound;
            }
        }

        public bool End()
        {
            return gameCount == games;
        }

        private bool EndGame()
        {
            if (blackTurn)
            {
                if (chessStatus.BlackWin())
                {
                    log.InfoFormat("Black Win! Game number: {0}", gameCount);
                    return true;
                }
                if (chessStatus.Dead())
                {
                    log.InfoFormat("Dead Game! Game number: {0}", gameCount);
                    return true;
                }

                return false;
            }
            else
            {
                if (chessStatus.WhiteWin())
                {
                    log.InfoFormat("White Win! Game number: {0}", gameCount);
                    return true;
                }
                if (chessStatus.Dead())
                {
                    log.InfoFormat("Dead Game! Game number: {0}", gameCount);
                    return true;
                }
                return false;
            }
        }

        private void ResetGame()
        {
            //Recreate chessStatus.
            Dictionary<string, object> chessParam = new Dictionary<string, object>();
            chessParam.Add("rows", gridSize);
            chessParam.Add("cols", gridSize);
            chessStatus = factory.CreateChessStatus(chessParam);

            oneGameRound = 0;
        }

        private void FirstStep()
        {
            int firstRow, firstCol;
            GenerateRandomPoint(gridSize, out firstRow, out firstCol);
            chessStatus.AddBlackPiece(CreateBlackPiece(firstRow, firstCol));
            blackTurn = false;
            ++oneGameRound;
        }

        private void PrepairChessBoard()
        {
            var sizeStr = System.Configuration.ConfigurationSettings.AppSettings["GridSize"];
            gridSize = Convert.ToInt32(sizeStr);
            strategyWrapper = new UnManagedWrapper.StrategyWrapper(trainerWrapper);

            Dictionary<string, object> chessParam = new Dictionary<string, object>();
            chessParam.Add("rows", gridSize);
            chessParam.Add("cols", gridSize);
            chessStatus = factory.CreateChessStatus(chessParam);
        }

        private void GenerateRandomPoint(int maxSize, out int row, out int col)
        {
            Random rand = new Random();
            row = rand.Next(0, maxSize);
            col = rand.Next(0, maxSize);
        }

        private IPiece CreateBlackPiece(int row, int column)
        {
            Dictionary<string, object> param = new Dictionary<string, object>();
            param.Add("row", row);
            param.Add("col", column);
            param.Add("color", Color.Black);

            return factory.CreatePiece(param);
        }

        private IPiece CreateWhitePiece(int row, int column)
        {
            Dictionary<string, object> param = new Dictionary<string, object>();
            param.Add("row", row);
            param.Add("col", column);
            param.Add("color", Color.Red);

            return factory.CreatePiece(param);
        }
    }
}
